import tkinter as tk
from tkinter import ttk, messagebox, filedialog
import requests
import re
import json
from bs4 import BeautifulSoup
import threading
import os


class MusicDownloaderGUI:
    def __init__(self, master):
        self.master = master
        master.title("音乐下载器")
        master.geometry("500x450")
        master.resizable(width=False, height=False)

        # 创建所有绑定变量
        self.song_name_var = tk.StringVar()
        self.status_var = tk.StringVar(value="准备就绪")
        self.download_path_var = tk.StringVar(value=os.path.join(os.path.expanduser("~"), "Downloads"))
        self.downloading = False
        self.cancel_flag = False

        # 创建界面
        self.create_widgets()
        self.center_window()

    def create_widgets(self):
        """创建所有界面组件"""
        # 主框架
        main_frame = tk.Frame(self.master, padx=20, pady=20)
        main_frame.pack(fill=tk.BOTH, expand=True)

        # 歌名输入（绑定回车事件）
        tk.Label(main_frame, text="歌曲名称:", font=('Arial', 12)).grid(row=0, column=0, sticky="w", pady=5)
        self.song_entry = tk.Entry(
            main_frame,
            textvariable=self.song_name_var,
            font=('Arial', 12),
            width=30
        )
        self.song_entry.grid(row=0, column=1, columnspan=2, sticky="ew", pady=5)
        self.song_entry.bind('<Return>', lambda event: self.start_download())  # 回车键绑定

        # 下载路径
        tk.Label(main_frame, text="下载路径:", font=('Arial', 12)).grid(row=1, column=0, sticky="w", pady=5)
        tk.Entry(
            main_frame,
            textvariable=self.download_path_var,
            font=('Arial', 12),
            width=25
        ).grid(row=1, column=1, sticky="ew", pady=5)
        tk.Button(
            main_frame,
            text="浏览",
            command=self.choose_download_path,
            width=8
        ).grid(row=1, column=2, padx=5)

        # 进度条框架（初始隐藏）
        self.progress_frame = tk.Frame(main_frame)
        self.progress_bar = ttk.Progressbar(
            self.progress_frame,
            orient="horizontal",
            length=400,
            mode="determinate"
        )
        self.progress_bar.pack(fill=tk.X, pady=5)

        # 进度标签
        self.progress_label = tk.Label(
            self.progress_frame,
            font=('Arial', 10)
        )
        self.progress_label.pack()

        # 按钮区域
        button_frame = tk.Frame(main_frame)
        button_frame.grid(row=4, column=0, columnspan=3, pady=20)

        self.download_button = tk.Button(
            button_frame,
            text="开始下载",
            command=self.start_download,
            width=15,
            bg="#4CAF50",
            fg="white"
        )
        self.download_button.pack(side=tk.LEFT, padx=10)

        self.cancel_button = tk.Button(
            button_frame,
            text="取消",
            command=self.cancel_download,
            width=15,
            state=tk.DISABLED
        )
        self.cancel_button.pack(side=tk.LEFT, padx=10)

        # 状态栏
        tk.Label(
            self.master,
            textvariable=self.status_var,
            bd=1,
            relief=tk.SUNKEN,
            anchor=tk.W,
            bg="#f0f0f0"
        ).pack(side=tk.BOTTOM, fill=tk.X)

    def choose_download_path(self):
        """选择下载路径"""
        path = filedialog.askdirectory()
        if path:
            self.download_path_var.set(path)

    def start_download(self):
        """开始下载歌曲"""
        song_name = self.song_name_var.get().strip()
        if not song_name:
            messagebox.showwarning("警告", "请输入歌曲名称！")
            return

        if self.downloading:
            messagebox.showwarning("警告", "当前已有下载任务在进行中！")
            return

        # 检查下载目录是否存在
        download_path = self.download_path_var.get()
        if not os.path.exists(download_path):
            try:
                os.makedirs(download_path)
            except:
                messagebox.showerror("错误", "无法创建下载目录！")
                return

        # 初始化下载状态
        self.downloading = True
        self.cancel_flag = False
        self.download_button.config(state=tk.DISABLED)
        self.cancel_button.config(state=tk.NORMAL)
        self.status_var.set(f"正在搜索歌曲: {song_name}...")

        # 显示进度条
        self.progress_frame.grid(row=2, column=0, columnspan=3, pady=10, sticky="ew")
        self.progress_bar["value"] = 0
        self.progress_label.config(text="准备下载...")

        # 在新线程中执行下载
        threading.Thread(
            target=self.download_song,
            args=(song_name, download_path),
            daemon=True
        ).start()

    def cancel_download(self):
        """取消下载"""
        self.cancel_flag = True
        self.status_var.set("正在取消下载...")

    def update_progress(self, value, text=None):
        """更新进度条"""
        self.progress_bar["value"] = value
        if text:
            self.progress_label.config(text=text)
        self.master.update_idletasks()

    def hide_progress(self):
        """隐藏进度条"""
        self.progress_frame.grid_forget()

    def reset_ui_state(self):
        """重置UI状态"""
        self.downloading = False
        self.cancel_flag = False
        self.download_button.config(state=tk.NORMAL)
        self.cancel_button.config(state=tk.DISABLED)
        self.hide_progress()

    def download_song(self, song_name, download_path):
        """下载歌曲的核心逻辑"""
        file_path = os.path.join(download_path, f"{song_name}.mp3")

        try:
            # 1. 搜索歌曲
            hex_str = song_name.encode('utf-8').hex().upper()
            hex_with_percent = "%" + '%'.join([hex_str[i:i + 2] for i in range(0, len(hex_str), 2)])

            url = "https://www.gequbao.com/s/" + hex_with_percent
            headers = {
                "user-agent": "Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/137.0.0.0 Safari/537.36"
            }

            self.status_var.set("正在搜索歌曲...")
            self.update_progress(10, "搜索歌曲中...")

            session = requests.Session()
            search_resp = session.get(url=url, headers=headers)
            soup = BeautifulSoup(search_resp.text, 'html.parser')
            soup_find = soup.find('div', class_='col-4 col-content text-right')

            if not soup_find:
                self.status_var.set("未找到歌曲！")
                self.handle_download_result(False, "未找到指定歌曲")
                return

            re_search = re.search(r'href="(.*?)"', str(soup_find)).group(1)
            song_url = "https://www.gequbao.com" + re_search

            # 2. 获取播放ID
            self.status_var.set("获取歌曲信息...")
            self.update_progress(30, "获取歌曲信息中...")

            song_resp = session.get(song_url)
            play_id = re.search(r'"play_id"\s*:\s*"([^"]+)"', song_resp.text).group(1)
            play_url = "https://www.gequbao.com/api/play-url"

            headers["x-requested-with"] = "XMLHttpRequest"
            data = {"id": play_id}

            # 3. 获取歌曲URL
            self.status_var.set("获取下载链接...")
            self.update_progress(50, "获取下载链接中...")

            play_resp = session.post(url=play_url, headers=headers, json=data)
            final_url = json.loads(play_resp.text)['data']['url']

            # 4. 下载歌曲
            self.status_var.set("开始下载歌曲...")
            self.update_progress(70, "准备下载...")

            # 带进度显示的下载
            response = requests.get(final_url, stream=True)
            total_size = int(response.headers.get('content-length', 0))
            downloaded = 0

            with open(file_path, 'wb') as f:
                for chunk in response.iter_content(chunk_size=8192):
                    if self.cancel_flag:
                        f.close()
                        if os.path.exists(file_path):
                            os.remove(file_path)
                        self.handle_download_result(False, "下载已取消")
                        return

                    if chunk:
                        f.write(chunk)
                        downloaded += len(chunk)
                        progress = 70 + (downloaded / total_size) * 30
                        self.update_progress(
                            progress,
                            f"下载中: {downloaded / 1024 / 1024:.2f}MB / {total_size / 1024 / 1024:.2f}MB"
                        )

            # 下载完成
            self.handle_download_result(True, f"下载完成: {file_path}")

        except requests.exceptions.RequestException as e:
            self.handle_download_result(False, f"网络错误: {str(e)}")
        except Exception as e:
            self.handle_download_result(False, f"下载失败: {str(e)}")
        finally:
            self.reset_ui_state()

    def handle_download_result(self, success, message):
        """处理下载结果"""
        self.status_var.set(message)

        if success:
            self.update_progress(100, "下载完成！")
            self.master.after(1000, self.hide_progress)  # 1秒后隐藏进度条
            messagebox.showinfo("完成", message)
        else:
            self.update_progress(0, "下载失败")
            self.master.after(500, self.hide_progress)  # 0.5秒后隐藏进度条
            if not self.cancel_flag:  # 如果不是用户取消的，显示错误
                messagebox.showerror("错误", message)

    def center_window(self):
        """窗口居中"""
        self.master.update_idletasks()
        width = self.master.winfo_width()
        height = self.master.winfo_height()
        x = (self.master.winfo_screenwidth() // 2) - (width // 2)
        y = (self.master.winfo_screenheight() // 2) - (height // 2)
        self.master.geometry(f'+{x}+{y}')


if __name__ == "__main__":
    root = tk.Tk()
    app = MusicDownloaderGUI(root)
    root.mainloop()